import os
import urllib3
import time
% if pkg.is_maintain:
import stat
import shutil
% endif
from conan.tools.env import VirtualBuildEnv
from conan import ConanFile
from conan.tools.scm import Git
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
from conan.tools.files import copy, update_conandata, chdir
from conan.errors import ConanInvalidConfiguration
% if language == "c:
from bingo.component.gen import GenComp
% endif
% if pkg.is_maintain:
from conans import tools
from conans.util.files import mkdir
from conan.tools.files import patch as apply_patch
from conans.errors import ConanException
% endif

urllib3.disable_warnings()

# 构建时由工具自动生成到业务仓，做为conanfile.py的基类参与组件构建
# 如需要调试，请修改模板文件（目录中存在python版本号、bingo集成开发环境应用名称，请适配）：
#   ~/.local/lib/python3.8/site-packages/bingo/component/template/conanbase.py.mako


class ConanBase(ConanFile):
    name = "${pkg.name}"
% if not pkg.is_maintain:
    version = "${pkg.version}"
% endif
    settings = "os", "compiler", "build_type", "arch"
    license = "Mulan PSL v2"
    generators = "CMakeDeps", "VirtualBuildEnv"
    language = "${language}"
    _cmake = None
    _codegen_version = ${codegen_version}
    options = {
        "asan": [True, False],
        "gcov": [True, False],
        "manufacture": [True, False],
    % for op, ctx in pkg.design_options.items():
        "${op}": [${",".join(("\"" + i + "\"") if isinstance(i, str) else str(i) for i in ctx["option"])}],
    % endfor
    }
    default_options = {
        "asan": False,
        "gcov": False,
        "manufacture": False,
    % for op, ctx in pkg.design_options.items():
        "${op}": ${("\"" + ctx["default"] + "\"") if isinstance(ctx["default"], str) else str(ctx["default"])},
    % endfor
    }

% if language == "c":
    def _codegen(self):
        args = ["-s", "mds/service.json"]
        gen = GenComp(args)
        gen.run(self._codegen_version)

% endif
% if pkg.is_maintain:
    def export_sources(self):
        patches = self.conan_data.get("patches", {}).get(self.version, [])
        for patch in patches:
            patch_file = patch.get("patch_file")
            if patch_file is None:
                continue
            # 与export_conandata_patches方法不同点：所有patches将从recipes_folder/../pacthes读取
            src = os.path.join(self.recipe_folder, "..", patch_file)
            dst = os.path.join(self.export_sources_folder, patch_file)
            mkdir(os.path.dirname(dst))
            shutil.copy2(src, dst)

    def source(self):
        git = tools.Git(verify_ssl=False)
        git.clone(**self.conan_data["sources"][self.version])

% else:
    def source(self):
        git = Git(self)
        sources = self.conan_data["sources"][self.version]
        if sources["url"] and sources["commit"]:
            git.clone(url=sources["url"], target=".")
            git.checkout(commit=sources["commit"])
        else:
            copy(self, "*", src=sources["pwd"], dst=".")
        % if language == "c":
        self._codegen()
        % endif

% endif
    def layout(self):
        cmake_layout(self)

    % if len(pkg.build_dependencies) > 0 or len(pkg.test_dependencies) > 0:
    def requirements(self):
        # 编译依赖
        % for build_dep in pkg.build_dependencies:
            % if "@" in build_dep:
        self.requires("${build_dep}")
            % endif
        % endfor

    % endif

    def export(self):
        copy(self, "conanbase.py", self.recipe_folder, self.export_folder)
        git = Git(self, self.recipe_folder)
        if git.is_dirty():
            update_conandata(self, {"sources": {self.version: {"commit": None, "url": None, "pwd": os.getcwd(), "timestamp": int(time.time())}}})
            return
        url = None
        commit = git.get_commit()
        branches = git.run("branch -r --contains {}".format(commit))
        remotes = git.run("remote")
        for remote in remotes.splitlines():
            if "{}/".format(remote) in branches:
                url = git.get_remote_url(remote)
                break
        if not url:
            update_conandata(self, {"sources": {self.version: {"commit": None, "url": None, "pwd": os.getcwd(), "timestamp": int(time.time())}}})
            return
        update_conandata(self, {"source": {self.version: {"commit": commit, "url": url}}})

    def export_source(self):
        copy(self, "service.json",
            src=os.path.join(self.recipe_folder, "mds"),
            dst=os.path.join(self.export_sources_folder, "mds"))

        copy(self, "conanbase.py",
            src=self.recipe_folder,
            dst=self.export_sources_folder)
        copy(self, "conandata.yml",
            src=self.export_folder,
            dst=self.export_sources_folder)

    def generate(self):
        tc = CMakeToolchain(self)
        tc.preprocessor_definitions["_FORTIFY_SOURCE"] = "2"

        tc.variables["BUILD_MANUFACTURE"] = self.options.manufacture
        % if len(pkg.design_options) > 0:
            % for op, _ in pkg.design_options.items():
        tc.variables["CONAN_DEFS_${op.upper()}"] = self.options.${op}
            % endfor
        % endif

        # 向CMAKE传递版本号信息
        version = self.version.split(".")
        if len(version) >= 1:
            tc.variables["PACKAGE_VERSION_MAJOR"] = version[0]
        if len(version) >= 2:
            tc.variables["PACKAGE_VERSION_MINOR"] = version[1]
        if len(version) >= 3:
            tc.variables["PACKAGE_VERSION_REVISION"] = version[2]
        # 设置额外编译选项或者重定义CFLAGS CXXFLAGS,也可以设置其他开关
        # 示例: os.environ['CFLAGS'] = f"{os.getenv('CFLAGS')} -fPIE"

        if self.settings.arch in ["armv8", "x86_64"]:
            tc.variables["CMAKE_INSTALL_LIBDIR"] = "usr/lib64"
        else:
            tc.variables["CMAKE_INSTALL_LIBDIR"] = "usr/lib"

        if self.options.asan or self.options.gcov:
            env = VirtualBuildEnv(self)
            env.generate()
        if self.options.get_safe("asan", False):
            print("Enable asan flags")
            asan_flags = "-fsanitize=address -fsanitize-recover=address,all -fno-omit-frame-pointer -fno-stack-protector -O0"
            self.buildenv_info.append("CFLAGS", asan_flags)
            self.buildenv_info.append("CXXFLAGS", asan_flags)
            self.buildenv_info.append("LDFLAGS", "-fsanitize=address")

        # GCOV 标志设置
        if self.options.get_safe("gcov", False):
            print("Enable gcov flags")
            gcov_flags = "-ftest-coverage -fprofile-arcs"
            self.buildenv_info.append("CFLAGS", gcov_flags)
            self.buildenv_info.append("CXXFLAGS", gcov_flags)
        # 配合generate添加宏定义
        tc.variables["CMAKE_TOOLCHAIN_FILE"] = "conan_toolchain.cmake"
        # rpath配置
        tc.variables["CMAKE_SKIP_BUILD_RPATH"] = True
        tc.variables["CMAKE_SKIP_RPATH"] = True
        tc.variables["CMAKE_SKIP_INSTALL_RPATH"] = True
        tc.variables["CMAKE_BUILD_WITH_INSTALL_RPATH"] = False
        tc.variables["CMAKE_INSTALL_RPATH_USE_LINK_PATH"] = False

        tc.generate()



% if pkg.is_maintain:
    @staticmethod
    def _get_patch_changed_files(patch_file):
        files = {}
        for line in open(patch_file):
            if not line.startswith("diff --git"):
                continue
            line = line.strip()
            chunk = line.split()
            a_file = chunk[-2][2:]
            b_file = chunk[-1][2:]
            files[a_file] = b_file
        return files

    def _revise_renamed_files(self, changed_files):
        for a_file, b_file in changed_files.items():
            if a_file != b_file:
                if a_file != "/dev/null" and b_file != "/dev/null":
                    os.rename(a_file, b_file)
                    cmd = f"git rm -f {a_file}"
                    self.run(cmd)
                elif a_file != "/dev/null":
                    cmd = f"git rm -f {a_file}"
                    self.run(cmd)
                    continue
            cmd = f"git add {b_file}"
            self.run(cmd)

%endif
    def build(self):
% if pkg.is_maintain:
        for patch in self.conan_data.get("patches", {}).get(self.version, []):
            patch_file = patch.get("patch_file")
            if not patch_file:
                continue
            real_path = os.path.join(self.folders.base_source, patch_file)
            print(f"Start patch file {patch_file}")
            changed_files = self._get_patch_changed_files(real_path)
            try:
                apply_patch(self, patch_file=real_path)
                self._revise_renamed_files(changed_files)
                cmd = f"git commit -m \"{patch_file}\""
                self.run(cmd)
            except ConanException:
                # 尝试还原文件修改
                for a_file, b_file in changed_files.items():
                    cmd = f"git checkout -- {a_file}"
                    self.run(cmd, ignore_errors=True)
                    cmd = f"git checkout -- {b_file}"
                    self.run(cmd, ignore_errors=True)
                cmd = "git am " + real_path
                self.run(cmd)
% endif
        cmake = CMake(self)
        cmake.configure(cli_args=["--no-warn-unused-cli"])
        cmake.build()
        cmake.install()

    def package(self):
        files_to_copy = [
            ("permissions.ini", None),
            ("mds/model.json", "include/mds"),
            ("mds/service.json", "include/mds"),
            ("build/*", "include"),
            ("mds/*", "usr/share/doc/openubmc/sensor/mds"),
            ("docs/*", "usr/share/doc/openubmc/sensor/docs"),
            ("*.md", "usr/share/doc/openubmc/sensor/docs"),
            ("*.MD", "usr/share/doc/openubmc/sensor/docs")
        ]

        for pattern, dst in files_to_copy:
            if pattern.startswith("mds/") or pattern.startswith("docs/"):
                src_dir = pattern.split("*")[0]
                if os.path.exists(src_dir):
                    copy(self, "*", src=src_dir, dst=dst or "", keep_path=True)
            elif os.path.exists(pattern.split("*")[0]):
                copy(self, pattern, src="", dst=dst or "", keep_path=True)

        with chdir(self, self.package_folder):
            for root, _, files in os.walk("."):
                for file in files:
                    if file.endswith(".lua") and not os.path.islink(os.path.join(root, file)):
                        file_path = os.path.join(root, file)
                        # self.run(f"luac -o {file_path} {file_path}")
                        # self.run(f"luac -s {file_path}")

    def package_info(self):
        self.runenv_info.append("PATH", os.path.join(self.package_folder, "opt/bmc/apps/${pkg.name}"))
        self.buildenv_info.append("PATH", os.path.join(self.package_folder, "opt/bmc/apps/${pkg.name}"))
    % if "application" in pkg.package_type and pkg.package_info is not None:
        % if len(pkg.package_info.get("bindirs", [])) > 0:
        self.cpp_info.bindirs = [${", ".join("\"" + i + "\"" for i in pkg.package_info["bindirs"])}]
            % for dir in pkg.package_info["bindirs"]:
        self.env_info.PATH.append(os.path.join(self.package_folder, "${dir}"))
            % endfor
        % endif
    % endif
    libs = []
    dirs = []
    for root, _, file in os.walk("."):
        for file in files:
            if file.endswith(".so") and file.startswith("lib"):
                if root.startswith("./"):
                    dirs.append(root[2:])
                else:
                    dirs.append(root)
                lib = file[3:-3]
                libs.append(lib)
                continue
            if file.endswith(".a") and file.startswith("lib"):
                if root.startswith("./"):
                    dirs.append(2:)
                else:
                    dirs.append(root)
                libs.append(lib)
                continue

    if len(dirs):
        self.cpp_info.component["${pkg.name}"].set_property("cmake_target_name", "${pkg.name}::${pkg.name}")
        self.cpp_info.component["${pkg.name}"].set_property("cmake_target_aliass", ["${pkg.name}::${pkg.name}"])
        self.cpp_info.component["${pkg.name}"].set_property("pkg_config_name", "${pkg.name}")
        self.cpp_info.component["${pkg.name}"].libs = libs
        self.cpp_info.component["${pkg.name}"].libdirs = dirs
        for dir in dirs:
            self.runenv_info.append("LD_LIBRARY_PATH", os.path.join(self.package_folder, dir))
            self.buildenv_info.append("LD_LIBRARY_PATH", os.path.join(self.package_folder, dir))

